#ifndef __LIBCPU_ARM32_H__
#define __LIBCPU_ARM32_H__

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>
#include <stdio.h>


/**
 * @brief Read a value from the ARMv7 P15 C1 register.
 * @return The value read from the register.
 */
static inline uint32_t arm32_read_p15_c1(void)
{
    uint32_t value;

    __asm__ __volatile__("mrc p15, 0, %0, c1, c0, 0" : "=r"(value) : : "memory");

    return value;
}


/**
 * @brief Write a value to the ARMv7 P15 C1 register.
 * @param value The value to write to the register.
 * @note This function also reads the register after writing to ensure the write is complete.
 */
static inline void arm32_write_p15_c1(uint32_t value)
{
    __asm__ __volatile__("mcr p15, 0, %0, c1, c0, 0" : : "r"(value) : "memory");
    arm32_read_p15_c1();
}


/**
 * @brief Enable global interrupts.
 * This function clears the I bit in the CPSR register to enable interrupts.
 * It is a critical operation that should be used with care, as it allows the CPU to respond to interrupt requests.
 * @param None
 * @return None
 */
static inline void arm32_interrupt_enable(void)
{
    uint32_t tmp;
    __asm__ __volatile__("mrs %0, cpsr\n"
                            "bic %0, %0, #(1<<7)\n"
                            "msr cpsr_cxsf, %0"
                            : "=r"(tmp)
                            :
                            : "memory");
}


/**
 * @brief Disable global interrupts.
 * This function sets the I bit in the CPSR register to disable interrupts.
 * @param None
 * @return None
 */
static inline void arm32_interrupt_disable(void)
{
    uint32_t tmp;

    __asm__ __volatile__("mrs %0, cpsr\n"
                            "orr %0, %0, #(1<<7)\n"
                            "msr cpsr_cxsf, %0"
                            : "=r"(tmp)
                            :
                            : "memory");
}


/**
 * @brief Enable the Memory Management Unit (MMU).
 * @param None
 * @return None
 */
static inline void arm32_mmu_enable(void)
{
    uint32_t value = arm32_read_p15_c1();
    arm32_write_p15_c1(value | (1 << 0));
}


/**
 * @brief Disable the Memory Management Unit (MMU).
 * @param None
 * @return None
 */
static inline void arm32_mmu_disable(void)
{
    uint32_t value = arm32_read_p15_c1();
    arm32_write_p15_c1(value & ~(1 << 0));
}


/**
 * @brief Enable the cache.
 * This function enables both the instruction cache and the data cache.
 * @param None
 * @return None
 */
static inline void arm32_dcache_enable(void)
{
    uint32_t value = arm32_read_p15_c1();
    arm32_write_p15_c1(value | (1 << 2));
}


/**
 * @brief Disable the data cache.
 * This function disables the data cache by clearing the corresponding bit in the P15 C1 register.
 * @param None
 * @return None
 */
static inline void arm32_dcache_disable(void)
{
    uint32_t value = arm32_read_p15_c1();
    arm32_write_p15_c1(value & ~(1 << 2));
}


/**
 * @brief Enable the instruction cache.
 * This function enables the instruction cache by setting the corresponding bit in the P15 C1 register.
 * @param None
 * @return None
 */
static inline void arm32_icache_enable(void)
{
    uint32_t value = arm32_read_p15_c1();
    arm32_write_p15_c1(value | (1 << 12));
}


/**
 * @brief Disable the instruction cache.
 * This function disables the instruction cache by clearing the corresponding bit in the P15 C1 register.
 * @param None
 * @return None
 */
static inline void arm32_icache_disable(void)
{
    uint32_t value = arm32_read_p15_c1();
    arm32_write_p15_c1(value & ~(1 << 12));
}


/**
 * @brief Set the base address of the Translation Table Base Register (TTB).
 * This function sets the base address for the translation table used by the MMU.
 * @param base The base address to set for the TTB.
 * @return None
 */
static inline void arm32_ttb_set(uint32_t base)
{
    __asm__ __volatile__("mcr p15, 0, %0, c2, c0, 0" : : "r"(base) : "memory");
}


/**
 * @brief Set the domain access control register.
 * This function sets the domain access control register, which defines the access permissions for different memory domains.
 * @param domain The domain access control value to set.
 * @return None
 */
static inline void arm32_domain_set(uint32_t domain)
{
    __asm__ __volatile__("mcr p15, 0, %0, c3, c0, 0" : : "r"(domain) : "memory");
}


/**
 * @brief Invalidate the entire TLB (Translation Lookaside Buffer).
 * This function invalidates all entries in the TLB, ensuring that the MMU will re-fetch translations from the translation table.
 * @param None
 * @return None
 */
static inline void arm32_tlb_invalidate(void)
{
    __asm__ __volatile__("mov r0, #0\n"
                            "mcr p15, 0, r0, c7, c10, 4\n"
                            "mcr p15, 0, r0, c8, c6, 0\n"
                            "mcr p15, 0, r0, c8, c5, 0\n"
                            :
                            :
                            : "r0");
}


#ifdef __cplusplus
}
#endif


#endif // __LIBCPU_ARM32_H__